#pragma once

#include "MeshDef.h"
#include "ParaBuilder.h"

#include <TColgp_SequenceOfPnt.hxx>

#include <random>
#include <time.h>

namespace MeshOp
{
	inline bool Tag(std::vector<std::vector<int>>& tags, int tag, int si, int sj, int depth)
	{
		int size = tags.size();
		auto checkValid = [&](int tag, int si, int sj)
		{
			for (int i = -2; i < 3; i += 2)
				for (int j = -2; j < 3; j += 2)
					if (tags[si + i][sj + j] != 0 && tags[si + i][sj + j] != tag)
						return false;
			return true;
		};
		if (depth > 0 && checkValid(tag, si, sj))
		{
			tags[si][sj] = tag;

			// 记录周围可标记的位置
			std::vector<std::vector<int>> pair;
			for (int i = -1; i < 2; i++)
			{
				for (int j = -1; j < 2; j++)
				{
					if (si + i < 2 || si + i > size - 3 || sj + j < 2 || sj + j > size - 3)
						continue;

					if ((i != 0 || j != 0) && tags[si + i][sj + j] == 0)
						pair.push_back({ si + i, sj + j });
				}
			}

			// 随机选择扩张方向，随机增加深度
			while (!pair.empty())
			{
				int i = rand() % pair.size();
				Tag(tags, tag, pair[i][0], pair[i][1], depth - 1);
				pair.erase(pair.begin() + i);
			}
		}

		return tags[si][sj] == tag;
	}

	// 生成随机带洞网格
	inline MeshPtr GenerateRandomMesh(int size, int holes, std::function<Mesh::Point(double u, double v)> f)
	{
		srand(time(0));
		MeshPtr mesh = std::make_unique<Mesh>();

		Mesh& meshRef = *mesh;
		meshRef.request_vertex_normals();
		meshRef.request_face_normals();
		meshRef.request_vertex_texcoords2D();

		// 去掉外围 2 层计算洞的深度
		int valid = size - 4;
		int depth = valid / holes / 2;

		// 移动有效区域，使洞的生成中心到边界的距离等于深度
		valid = size - depth * 2;

		// 标记空位置
		std::vector<std::vector<int>> tags(size, std::vector<int>(size, 0));
		for (int i = 0; i < holes; )
		{
			int tag = i + 1;
			int si = rand() % valid + depth;
			int sj = rand() % valid + depth;

			std::cout << "Pos: " << si << "," << sj << std::endl;
			if (Tag(tags, tag, si, sj, depth))
				i++;
		}

		double step = 1.0 / (size - 1);
		std::vector<Mesh::VertexHandle> vhandle1(size);
		std::vector<Mesh::VertexHandle> vhandle2(size);
		Mesh::VertexHandle h;
		for (int i = 0; i < size; i++)
		{
			for (int j = 0; j < size; j++)
			{
				if (tags[i][j] != 0)
					vhandle2[j] = h;
				else
				{
					vhandle2[j] = mesh->add_vertex(f(step * j, step * i));
					mesh->set_texcoord2D(vhandle2[j], { step * j, step * i });
				}

				if (i > 0 && j < size - 1)
				{
					std::vector<Mesh::VertexHandle> face_vhandles;

					face_vhandles.push_back(vhandle1[j]);
					face_vhandles.push_back(vhandle1[j + 1]);
					face_vhandles.push_back(vhandle2[j]);

					if (std::find(face_vhandles.begin(), face_vhandles.end(), h) == face_vhandles.end())
						mesh->add_face(face_vhandles);
				}

				if (i > 0 && j > 0)
				{
					std::vector<Mesh::VertexHandle> face_vhandles;

					face_vhandles.push_back(vhandle1[j]);
					face_vhandles.push_back(vhandle2[j]);
					face_vhandles.push_back(vhandle2[j - 1]);

					if (std::find(face_vhandles.begin(), face_vhandles.end(), h) == face_vhandles.end())
						mesh->add_face(face_vhandles);
				}
			}
			vhandle1 = vhandle2;
		}

		mesh->update_face_normals();
		mesh->update_vertex_normals();

		return mesh;
	}

	// 生成测试网格
	inline MeshPtr GenerateTestMesh(int size, int holes, std::function<int(TColgp_SequenceOfPnt&)> generator)
	{
		TColgp_SequenceOfPnt points;

		// M 是边界点个数
		int M = generator(points);
		int N = points.Size();

		// 从 8 * 8 网格开始，使用双三次样条
		int Nu = 25;
		int Nv = 25;
		int p = 3;
		int q = 3;

		// 目标权重
		double alpha = 1e-8;
		double beta = 1e-3;

		// 边界点和周围点加权
		double kappa = 1e-3;
		std::vector<double> w(N);
		for (int i = 0; i < N; i++)
		{
			if (i < M)
				w[i] = 1 - kappa;
			else
				w[i] = kappa;
		}

		// uv 参数，存放最后的参数化结果
		std::vector<double> U(N);
		std::vector<double> V(N);

		Para::ParaBuilder paraBuilder(points);
		Handle(Geom_BSplineSurface) surface = paraBuilder.Build(U, V, Nu, Nv, p, q, w, alpha, beta, 3);

		std::function<Mesh::Point(double, double)> f = [&](double u, double v) -> Mesh::Point
		{
			gp_Pnt P;
			surface->D0(u, v, P);

			double x = P.X();
			double y = P.Y();
			double z = P.Z();

			return Mesh::Point(x, y, z);
		};

		return GenerateRandomMesh(size, holes, f);
	}
}